
import { WsServer } from "tsrpc";
import { MsgPlayerInpFrame } from "../shared/gameClient/protocols/MsgPlayerInpFrame";
import { MsgPlayerSendSyncState } from "../shared/gameClient/protocols/MsgPlayerSendSyncState";
import { serviceProto as GameServiceProto, ServiceType as GameServiceType } from "../shared/gameClient/protocols/serviceProto";
import { MatchRequestServer } from "../shared/matchRequest/MatchRequestServer";
import { ENetworkState, IPlayerInfo } from "../shared/tsgf/player/IPlayerInfo";
import { IResult, Result } from "../shared/tsgf/Result";
import { EPlayerInputFrameType } from "../shared/tsgf/room/IGameFrame";
import { IRoomInfo } from "../shared/tsgf/room/IRoomInfo";
import { IPlayer } from "../shared/tsgfServer/auth/Models";
import { RoomHelper } from "../shared/tsgfServer/room/RoomHelper";
import { GameConnMgr } from "./GameConnMgr";
import { GameRoom } from "./GameRoom";
import { ClientConnection, GameMsgCall, GameWsServer } from "./GameServer";

export type Rooms = Map<string, GameRoom>;

export class GameServerAppRoomMgr {

    private gameWsServer: GameWsServer;
    protected gameConnMgr: GameConnMgr;
    protected appRooms: Map<string, Rooms> = new Map<string, Rooms>();
    protected gameServerNodeId: string;
    protected matchReqServer: MatchRequestServer;

    protected getPlayerCurrGameRoom(player?: IPlayer): GameRoom | null {
        if (player?.currRoomId) {
            let rooms = this.appRooms.get(player.authInfo.appId);
            if (rooms) {
                let room = rooms.get(player.currRoomId);
                return room ?? null;
            }
        }
        return null;
    }

    constructor(gameWsServer: GameWsServer, gameConnMgr: GameConnMgr, matchReqServer: MatchRequestServer, gameServerNodeId: string) {
        this.gameWsServer = gameWsServer;
        this.gameConnMgr = gameConnMgr;
        this.matchReqServer = matchReqServer;
        this.gameServerNodeId = gameServerNodeId;

        this.gameConnMgr.onConnDiconnect((connId, player) => {
            //掉线, 通知房间的其他玩家
            player.playerInfo.networkState = ENetworkState.OFFLINE;
            let room = this.getPlayerCurrGameRoom(player);
            if (room) {
                room.onlinePlayerConns.removeConnection(player.playerInfo.playerId);
                room.triggerChangePlayerNetworkState(player.playerInfo);
            }
            return true;
        });
        this.gameConnMgr.onConnReconnect(async (conn, player) => {
            //上线, 通知房间的其他玩家
            player.playerInfo.networkState = ENetworkState.ONLINE;
            let room = this.getPlayerCurrGameRoom(player);
            if (room) {
                await room.triggerChangePlayerNetworkState(player.playerInfo);
                room.onlinePlayerConns.addConnection(conn);
            }
        });
        this.gameConnMgr.onAuthedPlayerDisconnect((connId, player) => {
            //玩家下线了，就执行退出房间操作
            this.leaveRoom(player);
        });

        //统一侦听,并将消息传递给房间对象,而不是每个房间各自注册,因为房间变动太频繁
        this.gameWsServer.listenMsg('PlayerInpFrame', (call: GameMsgCall<MsgPlayerInpFrame>) => {
            let room = this.getPlayerCurrGameRoom(call.conn.currPlayer);
            if (room) {
                room.game.playerInpFrame(call.conn.currPlayer, EPlayerInputFrameType.Operates,
                    inpFrame => inpFrame.operates = call.msg.operates);
            }
        });
        this.gameWsServer.listenMsg("PlayerSendSyncState", (call: GameMsgCall<MsgPlayerSendSyncState>) => {
            if (call.conn.currPlayer?.currRoomId) {
                let rooms = this.appRooms.get(call.conn.currPlayer.authInfo.appId);
                if (rooms) {
                    let room = rooms.get(call.conn.currPlayer.currRoomId);
                    if (room) {
                        room.game.playerSendSyncState(call.conn.currPlayer, call.msg);
                    }
                }
            }
        });
    }


    /**
     * 获取应用下的房间集合
     * @date 2022/5/11 - 15:07:04
     *
     * @protected
     * @param {string} appId
     * @returns {Rooms}
     */
    protected getRooms(appId: string): Rooms {
        let rooms = this.appRooms.get(appId);
        if (!rooms) {
            rooms = new Map<string, GameRoom>();
            this.appRooms.set(appId, rooms);
        }
        return rooms;
    }
    /**
     * 获取或初始化房间（只要房间ID确实是分配给本游戏服务器的）
     * @date 2022/5/10 - 21:33:48
     *
     * @public
     * @async
     * @param {string} roomId
     * @returns {(Promise<IRoomInfo | null>)}
     */
    protected async getOrInitRoom(rooms: Rooms, roomId: string): Promise<GameRoom | null> {
        if (!roomId || roomId.length > 200) return null;

        let gameRoom: GameRoom | undefined | null = rooms.get(roomId);
        if (gameRoom) return gameRoom;

        let roomExInfo = await RoomHelper.extractRoomInfo(roomId, this.gameServerNodeId);
        if (!roomExInfo) return null;
        gameRoom = new GameRoom(roomExInfo.regInfo, roomExInfo.roomInfo, this.gameWsServer, this.gameConnMgr, this.matchReqServer);

        rooms.set(roomId, gameRoom);
        return gameRoom;
    }


    /**
     * 获取玩家当前所在的房间信息
     * @date 2022/5/11 - 15:43:33
     *
     * @public
     * @async
     * @param {IPlayer} player
     * @returns {(Promise<IRoomInfo | null>)}
     */
    public async getRoomInfo(player: IPlayer): Promise<IRoomInfo | null> {
        if (!player.currRoomId) return null;
        let rooms = this.getRooms(player.authInfo.appId);
        let gameRoom = await this.getOrInitRoom(rooms, player.currRoomId);
        if (!gameRoom) return null;
        return gameRoom.roomInfo;
    }
    /**
     * 获取玩家当前所在的房间操作对象
     * @date 2022/5/11 - 15:43:33
     *
     * @public
     * @async
     * @param {IPlayer} player
     * @returns {(Promise<IRoomInfo | null>)}
     */
    public async getGameRoom(player: IPlayer): Promise<GameRoom | null> {
        if (!player.currRoomId) return null;
        let rooms = this.getRooms(player.authInfo.appId);
        let gameRoom = await this.getOrInitRoom(rooms, player.currRoomId);
        if (!gameRoom) return null;
        return gameRoom;
    }



    /**
     * 玩家加入房间，会根据房间等的规则判断是否可以加入
     * @date 2022/5/10 - 22:43:36
     *
     * @public
     * @async
     * @param {IPlayer} player
     * @param {string} roomId
     * @returns {Promise<IResult<IRoomInfo>>}
     */
    public async joinRoom(player: IPlayer, roomId: string): Promise<IResult<IRoomInfo>> {
        if (player.currRoomId) {
            //之前已经在一个房间中的话,要先退出
            return Result.buildErr('请先退出之前的房间！', 2001);
        }
        let rooms = this.getRooms(player.authInfo.appId);
        let gameRoom = await this.getOrInitRoom(rooms, roomId);
        if (!gameRoom) {
            return Result.buildErr('不存在的房间ID！', 2002);
        }
        return await gameRoom.joinRoom(player);
    }


    /**
     * 解散房间
     * @date 2022/5/11 - 14:27:55
     *
     * @public
     * @async
     * @param {IPlayer} player 当前玩家
     * @param {string} roomId 房间ID
     * @returns {Promise<IResult<IRoomInfo>>}
     */
    public async dismissRoom(player: IPlayer, roomId: string): Promise<IResult<IRoomInfo>> {
        let rooms = this.getRooms(player.authInfo.appId);
        let gameRoom = await this.getOrInitRoom(rooms, roomId);
        if (!gameRoom) {
            return Result.buildErr('不存在的房间ID！');
        }
        let ret = await gameRoom.dismissRoom(player);
        if (!ret.succ) return ret;

        rooms.delete(gameRoom.roomInfo.roomId);
        gameRoom.dispose();

        return ret;
    }



    /**
     * 离开玩家当前所在的房间，如果没在或者房间已经关闭则是返回失败的结果
     * @date 2022/5/10 - 22:43:36
     *
     * @public
     * @async
     * @param {IPlayer} player
     * @returns {Promise<IResult<IRoomInfo>>}
     */
    public async leaveRoom(player: IPlayer): Promise<IResult<IRoomInfo>> {
        if (!player.currRoomId) {
            return Result.buildErr("当前不在房间中！");
        }
        let rooms = this.getRooms(player.authInfo.appId);
        let gameRoom = rooms.get(player.currRoomId);
        if (!gameRoom) {
            player.currRoomId = undefined;
            return Result.buildErr("房间已经关闭，无需退出！");
        }
        let ret = await gameRoom.leaveRoom(player);
        if (!ret.succ) return ret;

        if (gameRoom.isDismiss) {
            rooms.delete(gameRoom.roomInfo.roomId);
            gameRoom.dispose();
        }

        return ret;
    }
}